﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using System.Collections;
namespace WindowsGame4
{
    public class Physics
    {
        public static int nCollisions, nChecks;
        public Physics()
        {
        }
        public static void Collide(List<GameObject> objects)
        {
            nCollisions = nChecks = 0;
            for (int i = 0; i < objects.Count; i++)
            {
                GameObject obj_1 = (GameObject)objects[i];
                for (int j = i + 1; j < objects.Count; j++)
                {
                    GameObject obj_2 = (GameObject)objects[j];
                    if (obj_1.bCollides && obj_2.bCollides)
                    {
                        nChecks++;
                        if (obj_1 is ParticleEmitter && !(obj_2 is ParticleEmitter))
                            CollideParticles(obj_2, (ParticleEmitter)obj_1);
                        else if (obj_2 is ParticleEmitter && !(obj_1 is ParticleEmitter))
                            CollideParticles(obj_1, (ParticleEmitter)obj_2);
                        else if(!(obj_1 is ParticleEmitter) && !(obj_2 is ParticleEmitter))
                        {
                            if (Intersects(obj_1,obj_2))
                            {
                                Vector2 point = Intersect(obj_1, obj_2);
                                obj_1.React(obj_2, point);
                                obj_2.React(obj_1, point);
                                nCollisions++;
                            }
                        }
                    }
                }
            }
        }
        public static void CollideParticles(GameObject obj, ParticleEmitter emitter)
        {
            foreach (GameObject p in emitter.particles)
            {
                    nChecks++;
                    
                    if (Intersects(obj, p))
                    {
                        Vector2 point = Intersect(obj, p);
                        obj.React(p, point);
                        p.React(obj, point);
                        emitter.React(obj, point);
                    }
                    nCollisions++;
            }
        }
        public static Vector2 Intersect(GameObject o1, GameObject o2)
        {
            Vector2 point;
            if (o1.shape==GameObject.Shape.Circle)
                if (o2.shape == GameObject.Shape.Circle)
                    point = CCIntersect(o1, o2);
                else point = CRIntersect(o1, o2);
            else if (o2.shape == GameObject.Shape.Circle)
                point = CRIntersect(o2, o1);
            else point = RRIntersect(o1, o2);
            return point;
        }
        public static bool Intersects(GameObject o1, GameObject o2)
        {
            if (o1.shape == GameObject.Shape.Circle)
                if (o2.shape == GameObject.Shape.Circle)
                    return CCIntersects(o1, o2);
                else return CRIntersects(o1, o2);
            else if (o2.shape == GameObject.Shape.Circle)
                return CRIntersects(o2, o1);
            else return RRIntersects(o1, o2);
        }
        public static Vector2 CCIntersect(GameObject c1, GameObject c2)
        {
            float r1 = c1.GetRadius(), r2 = c2.GetRadius();
            Vector2 dir = c2.pos - c1.pos;
            Vector2 point = GetIntersection(c1, c2, r1, r2, dir);
            return point;
        }
        public static bool CCIntersects(GameObject c1, GameObject c2)
        {
            float r1 = c1.GetRadius(), r2 = c2.GetRadius();
            Vector2 dir = c2.pos - c1.pos;
            float distance = dir.Length();
            return AreCloser(r1, r2, distance);
        }
        public static bool CRIntersects(GameObject c, GameObject r)
        {
            Vector2 rdir = GameObject.GetDir(r.angle-180);
            Vector2 cdir = r.pos - rdir*r.texture.Width*r.scale.X*.5f - c.pos;
            Vector2 dir = cdir - rdir*Vector2.Dot(rdir, cdir);
            float r1 = c.GetRadius(), r2 = r.GetRayWidth();
            if (Vector2.Dot(rdir, cdir) > 0)
                return false;
            float distance = dir.Length();
            return AreCloser(r1,r2,distance);
        }
        public static bool RRIntersects(GameObject b1, GameObject b2)
        {
            return false;
        }
        public static Vector2 CRIntersect(GameObject c, GameObject r)
        {
            Vector2 rdir = GameObject.GetDir(r.angle);
            Vector2 dir = r.pos - c.pos;
            dir -= rdir * Vector2.Dot(rdir, dir);
            float r1 = c.GetRadius(), r2 = r.GetRayWidth();
            Vector2 point = GetIntersection(c, r, r1, r2, dir);
            return point;
        }
        public static Vector2 RRIntersect(GameObject b1, GameObject b2)
        {
            return new Vector2();
        }
        public static bool AreCloser(float r1, float r2, float d)
        {
            return r1 + r2 > d;
        }
        public static Vector2 GetIntersection(GameObject o1, GameObject o2, float r1, float r2, Vector2 dir)
        {
            float distance = dir.Length();
            float d = r1 + r2 - distance;
            if (distance > 0)
                dir.Normalize();
            else return o1.pos;
            Vector2 point = o1.pos + dir * (d * 0.5f+distance-r2);
            Vector2 dp = dir * d;
            //push out
            if (o1.bBlocks && o2.bBlocks
                && (o1.bMoves || o2.bMoves))
            {
                if (o1.bMoves)
                    if (o2.bMoves)
                    {
                        o1.pos -= dp * r1 / (r1 + r2);
                        o2.pos += dp * r2 / (r1 + r2);
                    }
                    else
                    {
                        o1.pos -= dp;
                    }
                else o2.pos += dp;
            }
            return point;
        }
    }
}
